#include "gamekit/sdl/SDLCanvas.h"

#include "gamekit/drawing/Canvas.h"
#include "gamekit/drawing/Bitmap.h"
#include "gamekit/Component.h"
#include "gamekit/Window.h"

#ifdef __WIN32__
#include <SDL.h>
#else
#include <SDL/SDL.h>
#endif

using namespace gamekit;
using namespace gamekit::sdl;

SDLCanvas::SDLCanvas(const SafePointer<SDLWindow>& window, const int& x, const int& y, const uint& width, const uint& height, const uint& bpp, const uint& zIndex)
	: Canvas(x, y, width, height, bpp, zIndex),
	m_window(window),
	m_surface(NULL)
{
}

SDLCanvas::SDLCanvas(const SafePointer<SDLWindow>& window, const int& x, const int& y, const SafePointer<Bitmap>& bitmap, const uint& zIndex)
	: Canvas(x, y, bitmap->width(), bitmap->height(), bitmap->bpp(), zIndex),
	m_window(window),
	m_surface(NULL)
{
}

SDLCanvas::~SDLCanvas()
{
}

uint SDLCanvas::getPixel(SDL_Surface* surface, const int& x, const int& y)
{
	int bpp = surface->format->BytesPerPixel;
	uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

	switch(bpp)
	{
	case 1:
		return *p;

	case 2:
		return *(Uint16 *)p;

	case 3:
#if GK_BIGENDIAN
		return p[0] << 16 | p[1] << 8 | p[2];
#else
		return p[0] | p[1] << 8 | p[2] << 16;
#endif

	case 4:
		return *(uint*)p;

	default:
		return 0;
	}
}

void SDLCanvas::setPixel(SDL_Surface* surface, const uint& pixel, const int& x, const int& y)
{
	int bpp = surface->format->BytesPerPixel;
	/* Here p is the address to the pixel we want to set */
	uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

	switch(bpp)
	{
	case 1:
		*p = (uint8)pixel;
		break;

	case 2:
		*(Uint16*)p = (Uint16)pixel;
		break;

	case 3:
#if GK_BIGENDIAN
		p[0] = (pixel >> 16) & 0xff;
		p[1] = (pixel >> 8) & 0xff;
		p[2] = pixel & 0xff;
#else
		p[0] = pixel & 0xff;
		p[1] = (pixel >> 8) & 0xff;
		p[2] = (pixel >> 16) & 0xff;
#endif
		break;

	case 4:
		*(Uint32*)p = pixel;
		break;
	}
}

void SDLCanvas::resize(const uint& width, const uint& height)
{
	setWidth(width);
	setHeight(height);
	init(width, height, bpp());
}

void SDLCanvas::applyMask(SafePointer<Bitmap> mask)
{
	if(width() != mask->width() || height() != mask->height())
	{
		return;
	}

	throw std::exception();
	/*
	SDL_Surface* maskSurface = mask->surface()->surface();
	SDL_Surface* surface = m_surface->surface();

	uint w = width();
	uint h = height();

	for(uint x = 0; x < w; ++x)
	{
		for(uint y = 0; y < h; ++y)
		{
			uint pixel = getPixel(surface, x, y);
			uint8 maskPixel = (uint8)getPixel(maskSurface, x, y);
			uint8 r = 0, g = 0, b = 0, a = 0;

			SDL_GetRGBA(pixel, surface->format, &r, &g, &b, &a);

			uint newPixel = SDL_MapRGBA(surface->format, r, g, b, maskPixel);

			setPixel(surface, newPixel, x, y);
		}
	}
	*/
}

void SDLCanvas::fill(const Color& color, const uint& x, const uint& y, const uint& width, const uint& height)
{
	if( (x + width > this->width()) || (y + height > this->height()) )
	{
		return;
	}

	SDL_Surface* surface = m_surface->surface();

	for(uint ix = x; ix < x + width; ++ix)
	{
		for(uint iy = y; iy < y + height; ++iy)
		{
			uint pixel = getPixel(surface, ix, iy);
			Color bg = getColor(surface, pixel);

			Color result = Color::alphaBlend(bg, color);

			pixel = getPixel(surface, result);
			setPixel(surface, pixel, ix, iy);
		}
	}
}

void SDLCanvas::draw(const SafePointer<Bitmap> bitmap, const int& x, const int& y)
{
	throw std::exception();
	/*
	SDL_Surface* surface = m_surface->surface();
	SDL_Surface* bitmapSurface = bitmap->surface()->surface();

	uint w = bitmap->width();
	uint h = bitmap->height();

	for(uint xi= 0; xi < w; ++xi)
	{
		for(uint yi = 0; yi < h; ++yi)
		{
			uint bitmapPixel = getPixel(bitmapSurface, xi, yi);
			uint canvasPixel = getPixel(surface, x + xi, y + yi);

			Color bitmap_color = getColor(bitmapSurface, bitmapPixel);
			Color canvas_color = getColor(surface, canvasPixel);

			Color color = Color::alphaBlend(canvas_color, bitmap_color);
			uint pixel = getPixel(surface, color);

			setPixel(surface, pixel, x + xi, y + yi);
		}
	}
	*/
}

void SDLCanvas::draw(const unsigned int& elapsed)
{
	GK_UNUSED(elapsed);

	SDL_Rect src;
	SDL_Surface* surface = m_surface->surface();

	src.x = src.y = 0;
	src.w = (uint16)surface->w;
	src.h = (uint16)surface->h;

	SDL_Rect dst;
	dst.x = (ushort)x();
	dst.y = (ushort)y();
	dst.w = (ushort)width();
	dst.h = (ushort)height();

	SDL_Surface* screen = m_window->screen()->surface();

	SDL_BlitSurface(surface, &src, screen, &dst);
}

void SDLCanvas::init(const int& width, const int& height, const int& bpp)
{
	SDL_Surface* screen = m_window->screen()->surface();

	SDL_Surface* temp = SDL_CreateRGBSurface(screen->flags, width, height, bpp, 
		screen->format->Rmask, screen->format->Gmask,
		screen->format->Bmask, screen->format->Amask);

	m_surface = new SDLSurface(SDL_DisplayFormatAlpha(temp));

	SDL_FreeSurface(temp);
}

Color SDLCanvas::getColor(SDL_Surface* surface, const uint& pixel)
{
	uint8 r = 0, g = 0, b = 0, a = 0;
	SDL_GetRGBA(pixel, surface->format, &r, &g, &b, &a);

	return Color(r, g, b, a);
}

uint SDLCanvas::getPixel(SDL_Surface* surface, const Color& color)
{
	return SDL_MapRGBA(surface->format, color.r(), color.g(), color.b(), color.a());
}
